/**
 * class  FreeSprite
 * 自定义Sprite
 * author haicent
 * email haicent@126.com
 * version 0.01
 */
package as3.component
{
	
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.*;

import as3.component.geom.IntRectangle;

	public class FreeSprite extends Sprite
	{
		protected var foregroundView:DisplayObject;
		protected var backgroundView:DisplayObject;
		
		protected var _width:Number = 0;
		protected var _height:Number = 0;
	
		
		private var masked:Boolean = false;
		protected var maskRect:IntRectangle;
		private var content:Sprite;
		private var maskShape:Shape;
		private var usingBitmap:Boolean;
		
		public function FreeSprite(masked:Boolean=false){
			super();
			focusRect = false;
			usingBitmap = false;
			tabEnabled = false;
			maskRect = new IntRectangle();
			setMasked(masked);
			
		}
		
		override public function get width():Number
		{
			if (ismasked())
			{
				return maskShape.width;
			} else {
				return super.width;
			}
		}
		
		override public function set width(value:Number):void
		{
			_width = value;
			if (ismasked())
			{
				maskRect.width = value;
				setMaskRect(maskRect);
			} else {
				super.width = value;
			}			
		}
		
		override public function get height():Number
		{
			if (ismasked())
			{
				return maskShape.height;
			} else {
				return super.height;
			}
		}
		
		override public function set height(value:Number):void
		{
			_height = value;
			if (ismasked())
			{
				maskRect.height = value;
				setMaskRect(maskRect);
			} else {
				super.height = value;
			}			
		}
		
		protected function d_addChild(child:DisplayObject):DisplayObject{
			return super.addChild(child);
		}
		
		protected function d_addChildAt(child:DisplayObject, index:int):DisplayObject{
			return super.addChildAt(child, index);
		}
		
		override public function addChildAt(child:DisplayObject, index:int):DisplayObject{
			if(usingBitmap){
				return content.addChildAt(child, index);
			}else{
				return d_addChildAt(child, index);
			}
		}
		
		protected function d_removeChild(child:DisplayObject):DisplayObject{
			return super.removeChild(child);
		}
		
		protected function isChild(child:DisplayObject):Boolean{
			if(usingBitmap){
				return child.parent == content;
			}else{
				return child.parent == this;
			}		
		}
		
		override public function removeChild(child:DisplayObject):DisplayObject{
			if(usingBitmap){
				return content.removeChild(child);
			}else{
				return d_removeChild(child);
			}
		}
		
		protected function d_removeChildAt(index:int):DisplayObject{
			return super.removeChildAt(index);
		}
		
		override public function removeChildAt(index:int):DisplayObject{
			if(usingBitmap){
				return content.removeChildAt(index);
			}else{
				return d_removeChildAt(index);
			}
		}
		
		protected function d_getChildAt(index:int):DisplayObject{
			return super.getChildAt(index);
		}
		
		override public function getChildAt(index:int):DisplayObject{
			if(usingBitmap){
				return content.getChildAt(index);
			}else{
				return d_getChildAt(index);
			}
		}
		
		protected function d_getChildByName(name:String):DisplayObject{
			return super.getChildByName(name);
		}
		
		override public function getChildByName(name:String):DisplayObject{
			if(usingBitmap){
				return content.getChildByName(name);
			}else{
				return d_getChildByName(name);
			}
		}
		
		protected function d_getChildIndex(child:DisplayObject):int{
			return super.getChildIndex(child);
		}
		
		override public function getChildIndex(child:DisplayObject):int{
			if(usingBitmap){
				return content.getChildIndex(child);
			}else{
				return d_getChildIndex(child);
			}
		}
		
		public function containsChild(child:DisplayObject):Boolean{
			if(usingBitmap){
				return child.parent.parent == this;
			}else{
				return child.parent == this;
			}		
		}
		
		protected function d_setChildIndex(child:DisplayObject, index:int):void{
			if (super.getChildIndex(child) != index)
			{
			    super.setChildIndex(child, index);
			}
		}
		
		override public function setChildIndex(child:DisplayObject, index:int):void{
			if(usingBitmap){
				if (content.getChildIndex(child) != index)
				{
				    content.setChildIndex(child, index);
				}
			}else{
				d_setChildIndex(child, index);
			}
		}
		
		protected function d_swapChildren(child1:DisplayObject, child2:DisplayObject):void{
			super.swapChildren(child1, child2);
		}
		
		override public function swapChildren(child1:DisplayObject, child2:DisplayObject):void{
			if(usingBitmap){
				content.swapChildren(child1, child2);
			}else{
				d_swapChildren(child1, child2);
			}
		}
		
		protected function d_swapChildrenAt(index1:int, index2:int):void{
			super.swapChildrenAt(index1, index2);
		}
		
		override public function swapChildrenAt(index1:int, index2:int):void{
			if(usingBitmap){
				content.swapChildrenAt(index1, index2);
			}else{
				d_swapChildrenAt(index1, index2);
			}
		}
		
		protected function get d_numChildren():int{
			return super.numChildren;
		}
		
		override public function get numChildren():int{
			if(usingBitmap){
				return content.numChildren;
			}else{
				return d_numChildren;
			}
		}
		
		public override function addChild(dis:DisplayObject):DisplayObject{
			if(foregroundView != null){
				if(usingBitmap){
					return content.addChildAt(dis, content.getChildIndex(foregroundView));
				}
				d_addChild(dis);
				d_swapChildren(dis, foregroundView);
				return dis;
			}
			if(usingBitmap){
				return content.addChild(dis);
			}
			return d_addChild(dis);
		}
		
		public function getHighestIndexUnderForeground():int{
			if(foregroundView == null){
				return numChildren;
			}else{
				return numChildren - 1;
			}
		}
			
		public function getLowestIndexAboveBackground():int{
			if(backgroundView == null){
				return 0;
			}else{
				return 1;
			}
		}
		
		override public function hitTestPoint(x:Number, y:Number, shapeFlag:Boolean=false):Boolean{
			if(ismasked()){
				return maskShape.hitTestPoint(x, y, shapeFlag);
			}else{
				return super.hitTestPoint(x, y, shapeFlag);
			}
		}
		
		override public function hitTestObject(obj:DisplayObject):Boolean{
			if(this.ismasked()){
				return maskShape.hitTestObject(obj);
			}else{
				return super.hitTestObject(obj);
			}
		}
		
		public function bringToTop(child:DisplayObject):void{
			var index:int = numChildren-1;
			if(foregroundView != null){
				if(foregroundView != child){
					index = numChildren-2;
				}
			}
			setChildIndex(child, index);
		}
		
		public function bringToBottom(child:DisplayObject):void{
			var index:int = 0;
			if(backgroundView != null){
				if(backgroundView != child){
					index = 1;
				}
			}
			setChildIndex(child, index);
		}
		
		protected function setBackgroundView(child:DisplayObject = null):void{
			if(child != backgroundView){
				if(backgroundView != null){
					removeChild(backgroundView);
				}
				backgroundView = child;
				if(child != null){
					addChildAt(child, 0);
				}
			}
		}
		
		protected function getBackgroundView():DisplayObject{
			return backgroundView;
		}
		
		protected function setForegroundView(child:DisplayObject = null):void{
			if(child != foregroundView){
				if(foregroundView != null){
					removeChild(foregroundView);
				}
				foregroundView = child;
				if(child != null){
					addChild(child);
				}
			}
		}
		
		protected function getForegroundView():DisplayObject{
			return foregroundView;
		}
	
		public function setMasked(m:Boolean):void{
			if(m != masked){
				masked = m;
				setUsingBitmap(cacheAsBitmap && masked);
				if(masked){
					checkCreateMaskShape();
					if(maskShape.parent != this){
						d_addChild(maskShape);
						mask = maskShape;
					}
					setMaskRect(maskRect);
				}else{
					if(maskShape != null && maskShape.parent == this){
						d_removeChild(maskShape);
					}
					mask = null;
				}
			}
		}
		
		protected function setMaskRect(b:IntRectangle):void{
			if(maskShape){
				maskShape.x = b.x;
				maskShape.y = b.y;
				maskShape.height = b.height;
				maskShape.width = b.width;
			}
			maskRect.setRect(b);
		}
		
		private function setUsingBitmap(b:Boolean):void{
			if(usingBitmap != b){
				usingBitmap = b;
				usingBitmapChanged();
			}
		}
		
		private function usingBitmapChanged():void{
			var children:Array;
			var n:int;
			var i:int;
			if(usingBitmap){
				if(!content){
					content = new Sprite();
					content.tabEnabled = false;
					content.mouseEnabled = false;
				}

				children = new Array();
				n = d_numChildren;
				for(i=0; i<n; i++){
					if(d_getChildAt(i) != maskShape){
						children.push(d_getChildAt(i));
					}
				}
				for(i=0; i<children.length; i++){
					content.addChild(children[i]);
				}
				
				d_addChild(content);
				if(masked){
					super.mask = null;
					content.mask = maskShape;
				}
			}else{
				d_removeChild(content);
				
				children = new Array();
				n = content.numChildren;
				for(i=0; i<n; i++){
					children.push(content.getChildAt(i));
				}
				for(i=0; i<children.length; i++){
					d_addChild(children[i]);
				}
				
				if(masked){
					content.mask = null;
					super.mask = maskShape;
				}
			}
		}
		
		override public function set mask(value:DisplayObject):void{
			if(usingBitmap){
				content.mask = value;
			}else{
				super.mask = value;
			}
		}
		
		override public function get mask():DisplayObject{
			if(usingBitmap){
				return content.mask;
			}else{
				return super.mask;
			}
		}
		
		override public function set filters(value:Array):void{
			super.filters = value;
			setUsingBitmap(super.cacheAsBitmap && masked);
		}
		
		override public function set cacheAsBitmap(value:Boolean):void{
			super.cacheAsBitmap = value;
			setUsingBitmap(value && masked);
		}
		
		private function checkCreateMaskShape():void{
			if(!maskShape){
				maskShape = new Shape();
				maskShape.graphics.beginFill(0);
				maskShape.graphics.drawRect(0, 0, 1, 1);
				maskShape.graphics.endFill();
			}
		}
		
		public function ismasked():Boolean{
			return masked;
		}
		
		override public function toString():String{
			var p:DisplayObject = this;
			var str:String = p.name;
			while(p.parent != null){
				var name:String = (p.parent == p.stage ? "Stage" : p.parent.name);
				p = p.parent;
				str = name + "." + str;
			}
			return str;
		}
	}
}